/**
 * @particulars 队列更新，实现数据的异步更新
 *  属性的修改以及$nextTick 的回调都维护在同一任务队列中，
 *  实现异步更新的同时，保证在$nexxtTick 中能及时正确地拿到修改后的属性值
 */

let queue = []; // 存储Watcher 的更新队列
let has = {}; // 用作去重的hash 结构
let pending = false; // 批处理，等待状态

// 2、执行Watcher 的更新
function flushSchedulerQueue() {
  // 拷贝一份，避免数组塌陷
  const watcherQueue = queue.slice(0);
  // 重置属性后，执行Watcher 更新视图
  queue = [];
  has = {};
  pending = false;

  // 遍历处理每一项Watcher，异步更新视图
  // 【......用户的watch、渲染Watcher】
  watcherQueue.forEach(watcher => watcher.run());
}

// 1、对Watcher 进行异步队列更新处理
export function queueWatcher(watcher) {
  const id = watcher.id;

  if (!has[id]) {
    queue.push(watcher);
    has[id] = true;

    // 不管update 执行多少次，我们都执行一次更新操作
    if (!pending) {
      // 异步更新视图
      nextTick(flushSchedulerQueue);
      // pending 设置为true 保证队列就执行一次
      pending = true;
    }
  }
}


let callbacks = []; // 存储回调函数的队列
let waiting = false;
let timerfunction; // 异步函数，保证回调函数异步执行

// 优雅降级，兼容ie
if (Promise) {
  // Promise 回调处理
  timerfunction = () => Promise.resolve().then(flushCallbacks);
} else if (MutationObserver) {
  // 创建并返回一个新的 MutationObserver 它会在指定的 DOM 发生变化时被调用
  const observer = new MutationObserver(flushCallbacks);
  const textNode = document.createTextNode(1);
  observer.observe(textNode, {
    characterData: true
  });

  timerfunction = () => textNode.textContent = 2;
} else if (setImmediate) {
  // ie 才有
  timerfunction = () => setImmediate(flushCallbacks);
} else {
  // 定时器处理
  timerfunction = () => setTimeout(flushCallbacks);
}

// 4、处理回调函数
function flushCallbacks() {
  const cbQueue = callbacks.slice(0);
  waiting = false;
  callbacks = [];

  cbQueue.forEach(cb => cb());
}

// 3、nextTick 并不单单是创建了一个异步任务，而是将这个任务维护到了队列中
export function nextTick(cb) {
  callbacks.push(cb);

  if (!waiting) {
    // 异步执行回调函数
    timerfunction();
    waiting = true;
  }
}